function forEach(obj, fn) {
  obj &&
    Object.keys(obj).forEach(key => {
      fn(obj[key], key)
    })
}
function isFunction(val) {
  return typeof val == 'function'
}
function isPlainObject(val) {
  return val && typeof val == 'object'
}

class Store {
  constructor(options) {
    this.state = options.state
    this.getters = options.getters
    this.mapMutation = new Map()
    this.mapAction = new Map()
    this.splitMutation(options)
    this.splitAction(options)
    this.splitModule(options)
  }
  splitMutation(options, parentKey = '') {
    forEach(options.mutations, (fn, key) => {
      const fullKey = parentKey ? '/' + parentKey + '/' + key : key
      if (!this.mapMutation.has(fullKey)) {
        this.mapMutation.set(fullKey, [])
      }
      this.mapMutation.get(fullKey).push(fn)
    })
  }
  splitAction(options, parentKey = '') {
    forEach(options.actions, (fn, key) => {
      const fullKey = parentKey ? '/' + parentKey + '/' + key : key
      if (!this.mapAction.has(fullKey)) {
        this.mapAction.set(fullKey, [])
      }
      this.mapAction.get(fullKey).push(fn)
    })
  }
  mergeState(module, parentKey) {
    // this.state
    this.state[parentKey] = module.state
  }
  mergeGetters(module) {
    // this.getters
    forEach(module.getters, (val, key) => {
      if (isFunction(val)) {
        this.getters[key] = val
      } else if (isPlainObject(val)) {
        this.mergeGetters(val)
      }
    })
  }
  splitModule(options, parentKey = '') {
    forEach(options.modules, (module, key) => {
      // state a.b.c
      // getters 全挂根，没有层级
      // mutaions /a/b
      // action /a/b
      const stateParentKey = parentKey ? parentKey + '.' + key : key
      const pathParentKey = parentKey ? '/' + parentKey + '/' + key : key
      this.mergeState(module, stateParentKey)
      this.mergeGetters(module)
      this.splitMutation(module, pathParentKey)
      this.splitAction(module, pathParentKey)
      this.splitModule(module, stateParentKey)
    })
  }
  buildVm(Vue) {
    const computed = {}
    forEach(this.getters, (fn, key) => {
      computed[key] = () => {
        fn(this.state)
      }
      Object.defineProperty(this, key, {
        get() {
          return vm[key]
        }
      })
    })
    const vm = new Vue({
      data: this.state,
      computed
    })
  }
  dispatch = (type, payload) => {
    // 异步 promise
    if (this.mapAction.has(type)) {
      const promiseArr = this.mapAction.get(type).map(fn => {
        return new Promise(resolve => {
          resolve(fn(this, payload))
        })
      })
      return Promise.all(promiseArr)
    }
  }
  commit = (type, payload) => {
    // 同步
    if (this.mapMutation.has(type)) {
      this.mapMutation.get(type).forEach(fn => {
        fn(this.state, payload)
      })
    }
  }
}

function install(Vue) {
  Vue.mixin({
    beforeCreate() {
      if (this.$options.store) {
        this.$store = this.$options.store
        this.$store.buildVm(Vue)
      } else if (this.$parent && this.$parent.$store) {
        this.$store = this.$parent.$store
      }
    }
  })
}

export default {
  Store,
  install
}

// mixin beforeCreate
// 改数据结构为 state,getters,mapMutation,mapAction，vue-router
// 响应式 state -> date 和 getters -> computed
// 实现dispatch 和 commit方法 / vue-router 做router-view
